package moon.excel.reader.impl;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import org.apache.commons.lang3.ArrayUtils;
import org.apache.poi.hssf.eventusermodel.EventWorkbookBuilder.SheetRecordCollectingListener;
import org.apache.poi.hssf.eventusermodel.FormatTrackingHSSFListener;
import org.apache.poi.hssf.eventusermodel.HSSFEventFactory;
import org.apache.poi.hssf.eventusermodel.HSSFListener;
import org.apache.poi.hssf.eventusermodel.HSSFRequest;
import org.apache.poi.hssf.eventusermodel.MissingRecordAwareHSSFListener;
import org.apache.poi.hssf.eventusermodel.dummyrecord.LastCellOfRowDummyRecord;
import org.apache.poi.hssf.eventusermodel.dummyrecord.MissingCellDummyRecord;
import org.apache.poi.hssf.record.BOFRecord;
import org.apache.poi.hssf.record.BlankRecord;
import org.apache.poi.hssf.record.BoolErrRecord;
import org.apache.poi.hssf.record.BoundSheetRecord;
import org.apache.poi.hssf.record.CellValueRecordInterface;
import org.apache.poi.hssf.record.FormulaRecord;
import org.apache.poi.hssf.record.LabelRecord;
import org.apache.poi.hssf.record.LabelSSTRecord;
import org.apache.poi.hssf.record.NumberRecord;
import org.apache.poi.hssf.record.Record;
import org.apache.poi.hssf.record.RowRecord;
import org.apache.poi.hssf.record.SSTRecord;
import org.apache.poi.hssf.record.StringRecord;
import org.apache.poi.poifs.filesystem.POIFSFileSystem;

import moon.excel.ExcelWraperException;
import moon.excel.IRecordInterceptor;
import moon.excel.reader.IExcelReader;
import moon.excel.util.ExcelUtil;

/**
 * 基于事件读取xls格式excel
 * 
 * @author Sunset
 *
 */
public class OldExcelEventReader implements HSSFListener, IExcelReader {
	private POIFSFileSystem fs;

	/** Should we output the formula, or the value it has? */
	private boolean outputFormulaValues = true;

	/*** excel中的字符串集合 */
	private SSTRecord sstRecord;
	private FormatTrackingHSSFListener formatListener;

	/** So we known which sheet we're on */
	private List<BoundSheetRecord> boundSheetRecords = new ArrayList<BoundSheetRecord>();

	private boolean outputNextStringRecord;

	private IRecordInterceptor<Object[]> recordInterceptor = null;

	private List<Object> tempRowdata = new ArrayList<Object>();

	private int rowIndex = 0;

	private int columnIndex = 0;

	private int rowBeginIndex = 0;

	private int columnBeginIndex = 0;

	private int minColumn = 0;

	private int[] rowIndexs;

	private Class<?>[] ColumnClasss;

	private Integer sheetIndex;

	private HSSFEventFactory factory = new HSSFEventFactory();

	private HSSFRequest request;

	private byte[] data;

	/**
	 * 
	 * @param fs
	 *            The POIFSFileSystem to process
	 * @throws IOException
	 */
	public OldExcelEventReader(InputStream is) throws IOException {
		ByteArrayOutputStream baos = new ByteArrayOutputStream();
		byte[] buffer = new byte[4096];
		int read = 0;
		while ((read = is.read(buffer)) != -1) {
			baos.write(buffer, 0, read);
		}
		data = baos.toByteArray();
		formatListener = new FormatTrackingHSSFListener(new MissingRecordAwareHSSFListener(this));
		request = new HSSFRequest();
		if (outputFormulaValues) {
			request.addListenerForAllRecords(formatListener);
		} else {
			SheetRecordCollectingListener workbookBuildingListener = new SheetRecordCollectingListener(formatListener);
			request.addListenerForAllRecords(workbookBuildingListener);
		}
	}

	/**
	 * Main HSSFListener method, processes events, and outputs the CSV as the file is processed.
	 */
	@Override
	public void processRecord(Record record) {
		String thisStr = null;
		switch (record.getSid()) {
		case BoundSheetRecord.sid:// New sheet
			boundSheetRecords.add((BoundSheetRecord) record);
			break;
		case BOFRecord.sid:// the BOFRecord can represent either the beginning of a sheet or the workbook
			break;
		case RowRecord.sid:// new Row
			break;
		case SSTRecord.sid:
			sstRecord = (SSTRecord) record;
			break;
		case BlankRecord.sid:
			thisStr = "";
			break;
		case BoolErrRecord.sid:
			thisStr = "";
			break;
		case FormulaRecord.sid:
			FormulaRecord frec = (FormulaRecord) record;
			if (outputFormulaValues) {
				if (Double.isNaN(frec.getValue())) {
					// Formula result is a string
					// This is stored in the next record
					outputNextStringRecord = true;
				} else {
					thisStr = formatListener.formatNumberDateCell(frec);
				}
			} else {
				thisStr = "function";
			}
			break;
		case StringRecord.sid:
			if (outputNextStringRecord) {
				// String for formula
				StringRecord srec = (StringRecord) record;
				thisStr = srec.getString();
				outputNextStringRecord = false;
			}
			break;
		case LabelRecord.sid:
			LabelRecord lrec = (LabelRecord) record;
			thisStr = lrec.getValue();
			break;
		case LabelSSTRecord.sid:
			LabelSSTRecord lsrec = (LabelSSTRecord) record;
			if (sstRecord == null) {
				thisStr = "";
			} else {
				thisStr = sstRecord.getString(lsrec.getSSTIndex()).toString();
			}
			break;
		case NumberRecord.sid:
			NumberRecord numrec = (NumberRecord) record;
			thisStr = formatListener.formatNumberDateCell(numrec);
			break;
		default:
			break;
		}
		if (sheetIndex != null && sheetIndex > 0 && !sheetIndex.equals(boundSheetRecords.size() - 1)) {
			return;
		}
		if (record instanceof CellValueRecordInterface) {
			CellValueRecordInterface rcRecord = (CellValueRecordInterface) record;
			rowIndex = rcRecord.getRow();
			columnIndex = rcRecord.getColumn();
		}
		if (record instanceof MissingCellDummyRecord) {
			thisStr = "";
		}
		if (columnIndex >= columnBeginIndex) {
			// 每一列
			if (thisStr != null && ColumnClasss != null && ColumnClasss.length == minColumn && ColumnClasss[columnIndex] != null) {
				try {
					if (!ColumnClasss[columnIndex].isAssignableFrom(thisStr.getClass())) {
						tempRowdata.add(ExcelUtil.convert(thisStr, ColumnClasss[columnIndex]));
					} else {
						tempRowdata.add(thisStr);
					}
				} catch (Exception e) {
					e.printStackTrace();
					tempRowdata.add(thisStr);
				}
			} else {
				tempRowdata.add(thisStr);
			}
		}
		if (record instanceof LastCellOfRowDummyRecord) {
			// 行结束
			Object[] r = tempRowdata.toArray();
			if (r.length != minColumn) {
				r = Arrays.copyOf(r, minColumn);
			}
			if (rowIndexs != null) {
				if (ArrayUtils.contains(rowIndexs, rowIndex)) {
					recordInterceptor.handle(r, rowIndex + 1);
					rowIndexs = ArrayUtils.removeElement(rowIndexs, rowIndex);
				}
				if (rowIndexs.length == 0) {
					throw new ExcelWraperException("搜索完毕");
				}
			} else {
				if (rowIndex >= rowBeginIndex) {
					recordInterceptor.handle(r, rowIndex + 1);
				}
			}
			tempRowdata.clear();
		}
	}

	@Override
	public void readSheet(Integer sheetIndex, int rowBeginIndex, int columnBeginIndex, int columnEndIndex, Class<?>[] classs, IRecordInterceptor<Object[]> interceptor) {
		reset(sheetIndex, rowBeginIndex, columnBeginIndex, columnEndIndex, null, classs, interceptor);
	}

	@Override
	public void readSheet(Integer sheetIndex, int columnBeginIndex, int columnEndIndex, int[] rowIndexs, Class<?>[] classs, IRecordInterceptor<Object[]> interceptor) {
		reset(sheetIndex, 0, columnBeginIndex, columnEndIndex, rowIndexs, classs, interceptor);
	}

	@Override
	public Object[] readRow(Integer sheetIndex, int rowIndex, int columnBeginIndex, int columnEndIndex, Class<?>[] classs) throws UnsupportedOperationException {
		final List<Object[]> list = new ArrayList<Object[]>();
		reset(sheetIndex, 0, columnBeginIndex, columnEndIndex, new int[] { rowIndex }, classs, new IRecordInterceptor<Object[]>() {

			@Override
			public void handle(Object[] t, int rowIndex) {
				list.add(t);
			}

		});
		return list.isEmpty() ? null : list.get(0);
	}

	private void reset(Integer sheetIndex, int rowBeginIndex, int columnBeginIndex, int columnEndIndex, int[] rowIndexs, Class<?>[] classs, IRecordInterceptor<Object[]> interceptor) {
		try {
			if (interceptor == null) {
				throw new RuntimeException("记录拦截器不能为空");
			}
			this.rowBeginIndex = rowBeginIndex;
			this.columnBeginIndex = columnBeginIndex;
			this.recordInterceptor = interceptor;
			this.ColumnClasss = classs;
			this.recordInterceptor = interceptor;
			this.minColumn = columnEndIndex - columnBeginIndex + 1;
			this.sheetIndex = sheetIndex;
			this.rowIndexs = rowIndexs;
			this.rowIndex = 0;
			this.columnIndex = 0;
			this.fs = new POIFSFileSystem(new ByteArrayInputStream(data));
			try {
				factory.processWorkbookEvents(request, fs);
			} catch (ExcelWraperException e) {
				if (!e.getMessage().equals("搜索完毕")) {
					e.printStackTrace();
				}
			}

		} catch (Exception e) {
			e.printStackTrace();
		}
	}

}
